/* factory.vala
 *
 * Copyright (C) 2008  Frederik 'playya' Sdun
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author:
 *     Frederik 'playya' Sdun <Frederik.Sdun@gmail.com>
 */
using GLib;

namespace Rob
{
    public static Factory actionfactory;
    public static Factory rulefactory;
    public class Factory: GLib.Object
    {
        public string libpath {get;set;}
        public string confpath{get;set;}
        private delegate Type RegisterPluginFunction ();
        private HashTable< string, GLib.Type > mappings;
        //Workaround until vala supports Factory<T: Rob.ILoadable>
        public HashTable< string, Rob.ILoadable> objects;
        
        construct
        {
            GLib.assert( Module.supported());
            this.confpath = null;

            stdout.printf("Create mapping\n");
            this.mappings = new GLib.HashTable<string, GLib.Type>( GLib.str_hash, GLib.str_equal );
            this.objects = new GLib.HashTable<string, Rob.ILoadable>( GLib.str_hash, GLib.str_equal );
        }
        public void load( string cpath )
        {
            GLib.message("Loading modules...\n");
            GLib.Dir confdir = null;
            try
            {
                confdir = GLib.Dir.open(cpath, 0);
            }
            catch ( GLib.FileError e)
            {
                GLib.error("Cannot open directory \"%s\": %s", this.confpath, e.message);
                // mode 755
                GLib.DirUtils.create_with_parents(this.confpath, 0x1ED);
                confdir = GLib.Dir.open(cpath, 0);
            }
            for( string file = Path.build_filename( cpath, confdir.read_name(), null ); file != null; file = Path.build_filename(cpath, confdir.read_name() ), null )
            {
                GLib.KeyFile kf = new GLib.KeyFile();
                try
                {
                    kf.load_from_file( file, GLib.KeyFileFlags.KEEP_COMMENTS|GLib.KeyFileFlags.KEEP_TRANSLATIONS);
                }
                catch ( GLib.KeyFileError ex )
                {
                    debug( "KeyfileError: %s", ex.message );
                    continue;
                }
                string[] groups = kf.get_groups();
                foreach( string g in groups )
                {
                    GLib.message("Loding module of %s\n", g);
                    string p = null;
                    try
                    {
                        p = kf.get_string(g, "libpath");
                    }
                    catch( GLib.KeyFileError e )
                    {
                        GLib.critical("Can't find libpath for %s", p);
                        continue;
                    }
                    if(!p.has_prefix(GLib.Path.DIR_SEPARATOR_S))
                        p = this.libpath.concat(p);
                    GLib.Module module = Module.open (p, GLib.ModuleFlags.BIND_LAZY);
                    if( module  != null )
                    {
                        GLib.message("Module loaded: %s\n", p );
                        string func_name = null;
                        try
                        {
                            func_name = kf.get_string( g, "ruleinit");
                        }
                        catch ( GLib.KeyFileError e )
                        {
                            GLib.critical("ruleinit for %s: %s\n", g, e.message);
                            continue;
                        }
                        GLib.message("Loading function %s\n",func_name);
                        void * function;
                        module.symbol(func_name, out function);
                        RegisterPluginFunction rpf = (RegisterPluginFunction) function;
                        if(rpf != null)
                        {
                            GLib.message("Getting type from %p", function);
                            GLib.Type type = rpf();
                            GLib.message("Type: %ul: %s", type, type.name());
                            mappings.insert(g, type);
                        }
                        else
                        {
                            GLib.critical("Functions %s not found in %s",func_name,p);
                        }
                    }
                    else
                    {
                        GLib.critical("could not load library: %s", libpath);
                    }
                }
            }
        }

        public Rob.ILoadable create( GLib.KeyFile kf, string name) throws GLib.KeyFileError
        {
            ILoadable t;
            t = objects.lookup(name);
            if( t == null)
            {
                string type;
                type  = kf.get_string(name, "type");
                GLib.Type objtype = mappings.lookup(type);
                t =  (Rob.ILoadable)GLib.Object.new(objtype);
                objects.insert( name, t );
            }
                
            t.load( kf, name );

            return t;
        }
        public void reset()
        {
            this.objects =  new GLib.HashTable<string, Rob.ILoadable>( GLib.str_hash, GLib.str_equal );
        }
    }
}
